/* Gauge - Performance Measurement Framework for Mobile Platforms
 * Copyright (C) 2013, Juha Hytonen <juha.a.hytonen@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.gauge.GaugeAgent;

import java.util.List;

import org.gauge.AbstractConnectionManager.ConnectionManagerCb;
import org.gauge.Benchmark;
import org.gauge.BenchmarkController;
import org.gauge.BenchmarkController.BenchmarkControllerCb;
import org.gauge.Log;
import org.gauge.MeasurementStorage;
import org.gauge.Probe;
import org.gauge.Protocol;
import org.gauge.Workload;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

/**
 * Executes benchmarks from BenchmarkFactory. The controller runs as an
 * AsyncTask separate from the UI thread.
 *
 * @see BenchmarkFactory
 * @see org.gauge.Benchmark
 * @see org.gauge.BenchmarkController
 */
public final class BenchmarkControllerService extends Service implements BenchmarkController, Runnable {

    private final IBinder binder = new BenchmarkControllerBinder();
    private BenchmarkControllerCb cb;
    private ConnectionManagerCb connmanCb;
    private ProbeController probeController;
    private WorkloadController workloadController;
    private ConnectionManager connectionManager;
    
    public class BenchmarkControllerBinder extends Binder {
    	BenchmarkController getService() {
            return BenchmarkControllerService.this;
        }
    }

    @Override
    public void onCreate() {
        probeController = new ProbeController();
        
        workloadController = new WorkloadController();
    	workloadController.SetContext(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
    	super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }    

    @Override
    public void SetCallbacks(BenchmarkControllerCb ctrlCb, ConnectionManagerCb connmanCb) {
        this.cb = ctrlCb;
        this.connmanCb = connmanCb;
    }
    
    @Override
    public void ConnectToServer(String ipAddress, int port) {
        connectionManager = new ConnectionManager();
        connectionManager.AddCallback(connmanCb);
        connectionManager.SetAddress(ipAddress);
        connectionManager.SetPort(port);
        connectionManager.Start();
        
        MeasurementStorage.Register(new RemoteMeasurementStorage(connectionManager));
        Log.Register(new RemoteLog(connectionManager));
    }
    
    @Override
    public void DisconnectFromServer() {
    	connectionManager.Disconnect();
    	connectionManager = null;
    	
    	MeasurementStorage.Register(null);
    	Log.Register(null);
    }
    
    @Override
    public void RunBenchmarks() {
    	Thread thread = new Thread(this);
    	thread.start();
    }
    
    public void run() {
    	cb.RunningBenchmarksEvent();
    	BenchmarkFactory benchmarkFactory = BenchmarkFactory.GetInstance();
    	List<Benchmark> benchmarks = benchmarkFactory.GetBenchmarks();
    	for(Benchmark benchmark : benchmarks){
    		Log.AddInfo("Setting up benchmark " + benchmark.GetName());
    		setup(benchmark);
    		Log.AddInfo("Running benchmark " + benchmark.GetName());
    		benchmark.Run();
    		Log.AddInfo("Tearing down benchmark " + benchmark.GetName());
    		teardown(benchmark);
    		Log.AddInfo("Benchmark " + benchmark.GetName() + " finished");
    	}
    	stop();
    }
    
    private void setup(Benchmark benchmark) {
    	String benchmarkName = benchmark.GetName();
    	
    	String msg = Protocol.CreateStartBenchmarkMsg(benchmarkName);
    	connectionManager.Send(msg);
		cb.RunningBenchmarkEvent(benchmarkName);
		
		benchmark.SetController(this);
		benchmark.Setup();
    }
    
    private void teardown(Benchmark benchmark) {
    	try {
    		benchmark.Teardown();
	    	probeController.StopAllProbes();
	    	workloadController.StopAllWorkloads();
    	}
    	catch(Exception e) {
    		Log.AddError(e.toString());
    	}
    }
    
    private void stop() {
    	String msg = Protocol.CreateEndBenchmarksMsg();
    	connectionManager.Send(msg);
    	cb.StoppedEvent();
    }

    @Override
    public void ReadProbe(Class<? extends Probe> probeClass) {
		Log.AddInfo("Reading probe " + probeClass.getName());
		try {
			probeController.ReadProbe(probeClass);
		}
		catch(Exception e) {
			Log.AddError(e.toString());
		}
    }
    
	@Override
	public void ReadProbeEvery(Class<? extends Probe> probeClass, int millisecs) {
		Log.AddInfo("Reading probe " + probeClass.getName() + " every " + String.valueOf(millisecs) + " millisecs");
		try {
			probeController.ReadProbeEvery(probeClass, millisecs);
		}
		catch(Exception e) {
			Log.AddError(e.toString());
		}
	}

	@Override
	public void RunWorkload(Class<? extends Workload> workloadClass) {
		Log.AddInfo("Running workload " + workloadClass.getName());
		try {
			workloadController.RunWorkload(workloadClass);
		}
		catch(Exception e) {
			Log.AddError(e.toString());
		}
	}

	@Override
	public void RunWorkloadFor(Class<? extends Workload> workloadClass, int millisecs) {
		Log.AddInfo("Running workload " + workloadClass.getName() + " for " + String.valueOf(millisecs) + " millisecs");
		try {
			workloadController.RunWorkloadFor(workloadClass, millisecs);
		}
		catch(Exception e) {
			Log.AddError(e.toString());
		}		
	}

	@Override
	public void RunWorkloadEvery(Class<? extends Workload> workloadClass, int millisecs) {
		Log.AddInfo("Running workload " + workloadClass.getName() + " every " + String.valueOf(millisecs) + " millisecs");
		try {
			workloadController.RunWorkloadEvery(workloadClass, millisecs);
		}
		catch(Exception e) {
			Log.AddError(e.toString());
		}			
	}
	
	@Override
    public void WaitForWorkloadToStop(Class<? extends Workload> workloadClass) {
		Log.AddInfo("Waiting for workload " + workloadClass.getName() + " to stop");
		try {
			workloadController.WaitForWorkloadToStop(workloadClass);
		}
		catch(Exception e) {
			Log.AddError(e.toString());
		}    	
    }

	@Override
	public void StopRunningWorkload(Class<? extends Workload> workloadClass) {
		Log.AddInfo("Stopping workload " + workloadClass.getName());
		try {
			workloadController.StopRunningWorkload(workloadClass);
		}
		catch(Exception e) {
			Log.AddError(e.toString());
		}    	
    }
	
	@Override
	public void BeforeNextActionWaitFor(int millisecs) {
		Log.AddInfo("Waiting " + String.valueOf(millisecs) + " millisecs for next action");
		org.gauge.Util.WaitFor(millisecs);
	}

}
